foitzik.dev // my collection of random things and thoughts

RocksDB support for SurrealDB with nix

I am currently working on non-vibe-coded project which is supposed to simplify the organisation of various items around the house. It is supposed to be very basic, so I obviously decided to use SQLite or Postgres or something conservative and compact as backend - right, right?

Of course something got the better of me and I decided to use SurrealDB. I am seriously considering switching this out later, but I am sticking to it for now until I have a complete MVP with mobile app and backend combined.

If one was smart they would use the in-memory adapter to get started quickly. However, I was used to SQLite and having a file persisted locally. Because I did not want to bootstrap a test db on every app startup. Therefore I opted to build surreal db with RocksDB support - welp. This is now not Rust only anymore and requires some llvm dependency. Since I am using nix for all my dev setups I intentionally keep the main system "pristine". So no libclang for cargo build to cling on. Googling this issue lead to some rather old nix github issues and I did not like the proposed solutions. I liked the general idea but it did not feel slim enough.

{
  description = "Top Secret";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    rust-overlay.url = "github:oxalica/rust-overlay";
    flake-utils.url = "github:numtide/flake-utils";
  };

  # TODO(juf): Add crane for
  #   1. Building
  #   2. Running tests
  #   3. Clippy/fmt/sec-adv db

  outputs =
    {
      self,
      nixpkgs,
      rust-overlay,
      flake-utils,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        overlays = [ (import rust-overlay) ];
        pkgs = import nixpkgs { inherit system overlays; };
      in
      {

        devShells.default =
          let
            stable-rust = pkgs.rust-bin.stable.latest;
            rust-core = stable-rust.default.override {
              extensions = [
                "rust-std"
                "rust-src"
              ];
            };
            stable-rust-analyzer = stable-rust.rust-analyzer;
          in
          with pkgs;
          mkShell {
            buildInputs = [
              libclang # <<< relevant part
              clang # <<< relevant part
              openssl # for convenience, using rusttls is fine too
              pkg-config # <<< relevant part
              rust-core
              stable-rust-analyzer
              cargo-expand
            ];
            LIBCLANG_PATH = "${libclang.lib}/lib"; # <<< relevant part

            # TODO(juf): using exec $SHELL does not really work. nix-shell -c $SHELL works well. Need to dig into why that is the case
            shellHook = ''
              echo "Entering system-specific shell <$SHELL>"
              #exec $SHELL (not properly working, you have to follow the description from the flake.nix and use nix-shell -c $SHELL
            '';
          };
      }
    );
}

Please do not hurt me for using flake-utils. I am going to migrate away from it, but I am not there yet. There might be an even more elegant or better way to do this, but I was already very happy with it. Let me quickly explain:

  1. This is for my dev shell only but the same solution can be used for building an artifact (binary/package)
  2. The build inputs specify the nix packages libclang, pkg-config, clang, where pkg-config warrants no explanation (if unclear read up on it).
    1. clang I am not 100% sure whether this is actually required, i.e., whether there are any calls to the binaries provided by it. I have not tested building without it - shame on me.
    2. libclang, must have.
  3. We then, in addition to providing these packages during the buildPhase add the contents of libclang or rather the path to the nix store location of the libclang package output as env variable to our dev shell. This is crucial so that libclangXXXX.so etc. can be found during cargo build when needed.